/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.util;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.iwindplus.boot.domain.constant.CommonConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.HeaderConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.SymbolConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.SystemConstant;
import com.iwindplus.boot.domain.vo.UserBaseVO;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jwt.util.DateUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ServerWebExchange;

/**
 * Http请求操作工具类.
 *
 * @author zengdegui
 * @since 2018/9/1
 */
@Slf4j
public class HttpsUtil extends HttpUtil {

    /**
     * 获取真实ip.
     *
     * @param exchange exchange
     * @return String
     */
    public static String getRealIp(ServerWebExchange exchange) {
        InetSocketAddress inetSocketAddress = exchange.getRequest().getRemoteAddress();
        return Optional.ofNullable(inetSocketAddress).map(InetSocketAddress::getAddress).map(InetAddress::getHostAddress).orElse(null);
    }

    /**
     * 获取语言.
     *
     * @param request 请求
     * @return Locale
     */
    public static Locale getLocale(HttpServletRequest request) {
        Locale locale;
        String language = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
        if (CharSequenceUtil.isNotBlank(language)) {
            locale = Locale.forLanguageTag(language);
        } else {
            locale = Locale.getDefault();
        }
        return locale;
    }

    /**
     * 获取语言.
     *
     * @param request 请求
     * @return Locale
     */
    public static Locale getLocale(HttpRequest request) {
        Locale locale;
        String language = request.getHeaders().getFirst(HttpHeaders.ACCEPT_LANGUAGE);
        if (CharSequenceUtil.isNotBlank(language)) {
            locale = Locale.forLanguageTag(language);
        } else {
            locale = Locale.getDefault();
        }
        return locale;
    }

    /**
     * 构建国际化语言.
     *
     * @param request 请求
     * @return String
     */
    public static String getLanguage(HttpServletRequest request) {
        String language = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
        if (CharSequenceUtil.isBlank(language)) {
            final Locale locale = Locale.getDefault();
            language = new StringBuilder(locale.getLanguage())
                .append(SymbolConstant.HORIZONTAL_LINE)
                .append(locale.getCountry()).toString();
        }
        return language;
    }

    /**
     * 构建国际化语言.
     *
     * @param request 请求
     * @return String
     */
    public static String getLanguage(HttpRequest request) {
        String language = request.getHeaders().getFirst(HttpHeaders.ACCEPT_LANGUAGE);
        if (CharSequenceUtil.isBlank(language)) {
            final Locale locale = Locale.getDefault();
            language = new StringBuilder(locale.getLanguage())
                .append(SymbolConstant.HORIZONTAL_LINE)
                .append(locale.getCountry()).toString();
        }
        return language;
    }

    /**
     * 获取完整的请求路径，包括：域名，端口，上下文访问路径.
     *
     * @return String
     */
    public static String getRequestUrl() {
        final HttpServletRequest request = getHttpServletRequest();
        if (Objects.nonNull(request)) {
            StringBuilder url = new StringBuilder(request.getRequestURL().toString());
            String contextPath = request.getServletContext().getContextPath();
            return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
        }
        return null;
    }

    /**
     * http获取请求头参数.
     *
     * @param request 请求
     * @return Map<String, String>
     */
    public static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> params = new LinkedHashMap<>(16);
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)
                || HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(key)) {
                continue;
            }
            params.put(key, request.getHeader(key));
        }
        return params;
    }

    /**
     * 获取map字节大小.
     *
     * @param map 请求头map
     * @return int
     */
    public static int getHeadersSize(Map<String, String> map) {
        if (MapUtil.isEmpty(map)) {
            return 0;
        }
        int headersSize = map.entrySet().stream().mapToInt(entry -> entry.getValue().getBytes(StandardCharsets.UTF_8).length).sum();
        // 将大小转换为KB
        double headersSizeInKb = headersSize / 1024.0;
        log.info("请求头大小={} KB", headersSizeInKb);
        return headersSize;
    }

    /**
     * http获取请求头参数.
     *
     * @param request 请求
     * @return Map<String, String>
     */
    public static Map<String, String> getHeaders(HttpRequest request) {
        Map<String, String> params = new LinkedHashMap<>(16);
        final Map<String, String> headerMap = request.getHeaders().toSingleValueMap();
        params.putAll(headerMap);
        params.remove(HttpHeaders.CONTENT_TYPE);
        params.remove(HttpHeaders.CONTENT_LENGTH);
        return params;
    }

    /**
     * http获取请求参数.
     *
     * @param request 请求
     * @return MultiValueMap<String, String>
     */
    public static MultiValueMap<String, String> getMultiParams(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
        parameterMap.forEach((key, values) -> {
            if (values.length > 0) {
                for (String value : values) {
                    parameters.add(key, value);
                }
            }
        });
        return parameters;
    }

    /**
     * http获取请求参数.
     *
     * @param request 请求
     * @return Map<String, String>
     */
    public static Map<String, String> getParams(HttpServletRequest request) {
        return HttpsUtil.getMultiParams(request).toSingleValueMap();
    }

    /**
     * http获取json请求参数.
     *
     * @param request 请求
     * @return String
     */
    public static String getJsonParams(HttpServletRequest request) {
        try (BufferedReader reader = request.getReader()) {
            StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            return buffer.toString();
        } catch (IOException ex) {
            log.error(ExceptionConstant.IO_EXCEPTION, ex);
        }
        return null;
    }

    /**
     * 获取请求.
     *
     * @return HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        try {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            if (Objects.nonNull(requestAttributes) && requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) {
                return servletRequestAttributes.getRequest();
            }
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);
        }
        return null;
    }

    /**
     * url 参数转码.
     *
     * @param value    值
     * @param encoding 编码
     * @return String
     */
    public static String urlEncode(String value, String encoding) {
        try {
            String encoded = URLEncoder.encode(value, encoding);
            return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
        } catch (UnsupportedEncodingException ex) {
            log.error(ExceptionConstant.UNSUPPORTED_ENCODING_EXCEPTION, ex);
        }
        return null;
    }

    /**
     * url 参数解码.
     *
     * @param value    值
     * @param encoding 编码
     * @return String
     */
    public static String urlDecode(String value, String encoding) {
        try {
            return URLDecoder.decode(value, encoding);
        } catch (UnsupportedEncodingException ex) {
            log.error(ExceptionConstant.UNSUPPORTED_ENCODING_EXCEPTION, ex);
        }
        return null;
    }

    /**
     * 参数转字符.
     *
     * @param params  参数
     * @param charset 编码
     * @return String
     */
    public static String paramToQueryString(Map<String, String> params, String charset) {
        if (MapUtil.isNotEmpty(params)) {
            StringBuilder paramString = new StringBuilder();
            boolean first = true;
            for (Iterator<Map.Entry<String, String>> it = params.entrySet().iterator(); it.hasNext(); first = false) {
                Object next = it.next();
                if (next instanceof Map.Entry) {
                    Map.Entry<String, String> p = it.next();
                    String key = p.getKey();
                    String value = p.getValue();
                    if (!first) {
                        paramString.append(CommonConstant.SymbolConstant.LOGICAL_AND);
                    }
                    paramString.append(urlEncode(key, charset));
                    if (Objects.nonNull(value)) {
                        paramString.append(CommonConstant.SymbolConstant.EQUAL).append(urlEncode(value, charset));
                    }
                }
            }
            return paramString.toString();
        }
        return null;
    }

    /**
     * 请求头iso88591转utf-8编码.
     *
     * @param headers 请求头
     */
    public static void convertHeaderCharsetFromIso88591(Map<String, String> headers) {
        convertHeaderCharset(headers, StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8);
    }

    /**
     * 请求头utf-8转iso88591编码.
     *
     * @param headers 请求头
     */
    public static void convertHeaderCharsetToIso88591(Map<String, String> headers) {
        convertHeaderCharset(headers, StandardCharsets.UTF_8, StandardCharsets.ISO_8859_1);
    }

    private static void convertHeaderCharset(Map<String, String> headers, Charset fromCharset, Charset toCharset) {
        Iterator<Map.Entry<String, String>> it = headers.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> header = it.next();
            if (Objects.nonNull(header.getValue())) {
                header.setValue(new String(header.getValue().getBytes(fromCharset), toCharset));
            }
        }
    }

    /**
     * 判读是否是json请求.
     *
     * @param request 请求
     * @return boolean
     */
    public static boolean isJsonRequest(HttpServletRequest request) {
        String header = request.getHeader(HttpHeaders.CONTENT_TYPE);
        return CharSequenceUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }

    /**
     * 判读是否是ajax请求.
     *
     * @param request 请求
     * @return boolean
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        return CharSequenceUtil.equalsIgnoreCase(CommonConstant.HeaderConstant.XML_HTTP_REQUEST,
            request.getHeader(HeaderConstant.X_REQUESTED_WITH));
    }

    /**
     * 根据请求头获取用户代理信息.
     *
     * @param request 请求
     * @return boolean
     */
    public static UserAgent getUserAgent(HttpServletRequest request) {
        return UserAgentUtil.parse(request.getHeader(HttpHeaders.USER_AGENT));
    }

    /**
     * http响应信息输出.
     *
     * @param response       响应
     * @param httpStatusCode http状态码
     * @param data           数据
     */
    public static void responseData(HttpServletResponse response, HttpStatusCode httpStatusCode, Object data) {
        String resultData;
        if (data instanceof String result) {
            resultData = result;
        } else {
            resultData = JacksonUtil.toJsonStr(data);
        }
        PrintWriter writer = null;
        try {
            response.setCharacterEncoding(Charset.defaultCharset().name());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setStatus(httpStatusCode.value());
            writer = response.getWriter();
            writer.write(resultData);
            writer.flush();
        } catch (IOException ex) {
            log.error(ExceptionConstant.IO_EXCEPTION, ex);
        } finally {
            if (Objects.nonNull(writer)) {
                writer.close();
            }
        }
    }

    /**
     * http响应信息输出.
     *
     * @param response       响应
     * @param responseEntity 响应实体
     */
    public static void responseData(HttpServletResponse response, ResponseEntity<Object> responseEntity) {
        HttpsUtil.responseData(response, responseEntity.getStatusCode(), responseEntity.getBody());
    }

    /**
     * 获取用户信息.
     *
     * @param authorization 授权信息
     * @return UserBaseVO
     */
    public static UserBaseVO getUserInfo(String authorization) {
        JWSObject jwsObject = null;
        try {
            jwsObject = JWSObject.parse(authorization);
        } catch (ParseException ex) {
            log.error(ExceptionConstant.PARSE_EXCEPTION, ex);
        }
        if (Objects.isNull(jwsObject)) {
            return null;
        }
        final Payload payload = jwsObject.getPayload();
        final Map<String, Object> payloadMap = payload.toJSONObject();
        final Date expDate = DateUtils.fromSecondsSinceEpoch((Long) payloadMap.get(SystemConstant.EXP));
        if (expDate.before(new Date())) {
            return null;
        }

        return JacksonUtil.parseObject(payload.toString(), UserBaseVO.class);
    }
}
